package cn.dansj.common.utils.jdbc.jdbcTemplate;

import cn.dansj.common.utils.jdbc.ColumnRowMapper;
import cn.dansj.common.utils.json.JSONUtils;
import cn.dansj.common.utils.lang.NotNull;
import cn.dansj.common.utils.transfer.ArrayUtils;
import cn.dansj.common.utils.transfer.Verification;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.rowset.SqlRowSet;
import org.springframework.jdbc.support.rowset.SqlRowSetMetaData;

import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@SuppressWarnings("SqlSourceToSinkFlow")
public class JdbcTemplate extends org.springframework.jdbc.core.JdbcTemplate {
    /**
     * java.sql.Types
     */
    private final static List<Integer> NoPrecisionDataType = ArrayUtils.asList(91, 92, 93);

    public JdbcTemplate() {
        super();
    }

    public JdbcTemplate(DataSource dataSource) {
        super(dataSource);
    }

    public JdbcTemplate(DataSource dataSource, boolean lazyInit) {
        super(dataSource, lazyInit);
    }

    @Override
    @NotNull
    public List<Map<String, Object>> queryForList(@NotNull String sql) throws DataAccessException {
        return this.query(sql, this.getColumnMapRowMapper());
    }

    @Override
    @NotNull
    public <T> List<T> queryForList(@NotNull String sql, @NotNull Class<T> elementType) throws DataAccessException {
        return this.query(sql, this.getSingleColumnRowMapper(elementType));
    }

    @Override
    @NotNull
    protected <T> RowMapper<T> getSingleColumnRowMapper(@NotNull Class<T> requiredType) {
        return new ColumnRowMapper<>(requiredType);
    }

    @Override
    @NotNull
    protected RowMapper<Map<String, Object>> getColumnMapRowMapper() {
        return new ColumnRowMapper<>();
    }

    @Override
    @NotNull
    public <T> T queryForObject(@NotNull String sql, @NotNull Class<T> requiredType, Object... args) throws DataAccessException {
        return JSONUtils.toObj(queryForMap(sql, args), requiredType);
    }

    @Override
    public <T> T queryForObject(@NotNull String sql, @NotNull Class<T> requiredType) throws DataAccessException {
        return JSONUtils.toObj(queryForMap(sql), requiredType);
    }

    @Override
    @NotNull
    public Map<String, Object> queryForMap(@NotNull String sql) throws DataAccessException {
        List<Map<String, Object>> results = this.query(sql, this.getColumnMapRowMapper());
        if (Verification.checkNotNull(results)) {
            return results.iterator().next();
        } else {
            return new HashMap<>();
        }
    }

    @NotNull
    public Map<String, Object> queryForMap(@NotNull String sql, Object... args) throws DataAccessException {
        try {
            return super.queryForMap(sql, args);
        } catch (Exception e) {
            return new HashMap<>();
        }
    }

    public List<Map<String, Object>> getMetaColumnData(String sql) {
        return parseRowSet(queryForRowSet(transferSqlToFalse(sql)));
    }

    public List<Map<String, Object>> getMetaColumnData(String sql, Object[] args, int[] argTypes) {
        return parseRowSet(queryForRowSet(transferSqlToFalse(sql), args, argTypes));
    }

    public List<Map<String, Object>> getMetaColumnData(String sql, Object... args) {
        return parseRowSet(queryForRowSet(transferSqlToFalse(sql), args));
    }

    private String transferSqlToFalse(String sql) {
        return "select * from (" + sql + ") t where 1=0";
    }

    private List<Map<String, Object>> parseRowSet(SqlRowSet sqlRowSet) {
        //获取字段列
        SqlRowSetMetaData rowSetMetaData = sqlRowSet.getMetaData();
        //获取列总数
        int columnCount = rowSetMetaData.getColumnCount();
        List<Map<String, Object>> column = new ArrayList<>();
        for (int i = 1; i <= columnCount; i++) {
            final String columnName = rowSetMetaData.getColumnName(i);
            final String columnTypeName = rowSetMetaData.getColumnTypeName(i).toLowerCase();
            final int precision = rowSetMetaData.getPrecision(i);
            final int scale = rowSetMetaData.getScale(i);
            final String catalogName = rowSetMetaData.getCatalogName(i);
            final String schemaName = rowSetMetaData.getSchemaName(i);
            final int columnDisplaySize = rowSetMetaData.getColumnDisplaySize(i);
            final String columnLabel = rowSetMetaData.getColumnLabel(i);
            final String display = NoPrecisionDataType.contains(rowSetMetaData.getColumnType(i)) ? columnTypeName : columnTypeName + "(" + precision + (scale > 0 ? "," + scale : "") + ")";
            column.add(JSONUtils.toJSONObject(JSONUtils.from("column", columnName, "type", columnTypeName, "precision", precision, "scale", scale, "displayType", display, "catalog", catalogName, "schema", schemaName)));
        }
        return column;
    }
}
